package com.sojson.config.mybatis.aspect;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import com.sojson.aspect.PointcutsBase;
import com.sojson.base.impl.BaseListDto;
import com.sojson.base.impl.BaseUser;
import com.sojson.config.mybatis.annotation.DataScope;
import com.sojson.constant.Constant;
import com.sojson.constant.ConstantServlet;
import com.sojson.constant.DataScopeType;
import com.sojson.util.StringUtil;
import com.sojson.util.param.ParamUtil;
import com.sojson.util.token.TokenUtil;

/**
 * 数据过滤处理
 *
 * @author lyh
 */
@Aspect
public class DataScopeAspect2 implements PointcutsBase {

    /** 数据权限过滤关键字 */
    public static final String DATA_SCOPE = "dataScope";
    private static final String ANNO_PACKAGE_PATH = ConstantServlet.PACKAGE_DEFAULT_BASE + ".config.mybatis.annotation";

    @Pointcut("@annotation(" + ANNO_PACKAGE_PATH + ".DataScope) && (" + POINT_SERVICE + ")")
    public void pointCutAnno() {}

    /** 配置织入点 */
    @Before("pointCutAnno()")
    public void doBefore(JoinPoint point) throws Throwable {
        handleDataScope(point);
    }

    /**
     * 处理方法
     * 
     * @param joinPoint
     * @throws IOException
     */
    protected void handleDataScope(final JoinPoint joinPoint) throws IOException {
        // 获得注解
        DataScope controllerDataScope = getAnnotationLog(joinPoint);
        if (controllerDataScope.isDefault()) {
            return;
        }

        // 获取当前的用户
        boolean login = ParamUtil.isLogin();
        if (login) {
            BaseUser user = TokenUtil.getUser();
            // 如果是超级管理员，则不过滤数据
            if (!ParamUtil.isAdminSuper()) {
                dataScopeFilter(joinPoint, user, controllerDataScope);
            }
        }
    }

    /**
     * 数据范围过滤
     *
     * @param joinPoint 切点
     * @param user      用户
     * @param dataScope 权限注解
     * @throws IOException 
     */
    public static void dataScopeFilter(JoinPoint joinPoint, BaseUser user, DataScope dataScope) throws IOException {
        String sqlStr = new String();
        StringBuilder sqlString = new StringBuilder();

        // 部门权限
        Integer scope = user.getDataScope();
        String deptAlias = dataScope.deptAlias();
        String deptField = dataScope.deptField();
        if (StringUtil.isNotBlank(deptField)) {
            if (DataScopeType.DATA_SCOPE_ALL == scope) {
            } else if (DataScopeType.DATA_SCOPE_CUSTOM == scope) {
                sqlString.append(StringUtil.format("{} IN (", buildColumn(deptAlias, deptField)));
                getDeptSql(sqlString, TokenUtil.getDepts());
            } else if (DataScopeType.DATA_SCOPE_DEPT == scope) {
                sqlStr = StringUtil.format("{} = '{}'", buildColumn(deptAlias, deptField), user.getDeptId());
            } else if (DataScopeType.DATA_SCOPE_DEPT_AND_CHILD == scope) {
                sqlString.append(StringUtil.format("{} IN (", buildColumn(deptAlias, deptField)));
                getDeptSql(sqlString, TokenUtil.getDepts());
            }
        }

        // 个人权限
        String userField = dataScope.userField();
        if (StringUtil.isNotBlank(userField) && DataScopeType.DATA_SCOPE_SELF == scope) {
            String userAlias = dataScope.userAlias();

            sqlString.append(StringUtil.format("{} = '{}'", buildColumn(userAlias, userField), user.getId()));
        }

        // 是否查询空值
        boolean deptIsNull = dataScope.deptIsNull();
        if (StringUtil.isNotBlank(deptField) && deptIsNull) {
            if (sqlStr.length() != 0) {
                sqlString.append(" OR ");
            }

            sqlString.append(StringUtil.format("{} IS NULL", buildColumn(deptAlias, deptField)));
        }

        // 角色权限
        String roleField = dataScope.roleField();
        if (StringUtil.isNotBlank(roleField)) {
            String roleAlias = dataScope.roleAlias();

            if (sqlStr.length() != 0) {
                sqlString = new StringBuilder("(").append(sqlString).append(")");
                sqlString.append(" AND ");
            }
            sqlString.append(StringUtil.format("{} IN (", buildColumn(roleAlias, roleField)));
            getDeptSql(sqlString,
                dataScope.roleIsAll() ? TokenUtil.getToken().getRoleIdsExist() : TokenUtil.getRoleIds());
        }

        if (StringUtil.isNotBlankString(sqlString.toString())) {
            Object params = joinPoint.getArgs()[0];
            if (StringUtil.isNotBlankObject(params) && params instanceof BaseListDto) {
                BaseListDto baseEntity = (BaseListDto)params;
                baseEntity.getParams().put(DATA_SCOPE, " AND (" + sqlStr + ") ");
            }
        }
    }

    /**
     * 解析Sql
     * 
     * @param sqlString
     * @param perms
     * @return
     */
    private static StringBuilder getDeptSql(StringBuilder sqlString, Set<String> perms) {
        for (String perm : perms) {
            sqlString.append(Constant.DYH).append(perm).append(Constant.DYH).append(",");
        }
        return sqlString.append(")");
    }

    /**
     * 构建Column
     * 
     * @param tableAlias    表别名
     * @param columnName    字段名称
     * @return
     */
    public static String buildColumn(String tableAlias, String columnName) {
        if (StringUtil.isNotBlank(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }

        return columnName;
    }

    /**
     * 是否存在注解，如果存在就获取
     * 
     * @param joinPoint
     * @return
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature)signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }

}